{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Azure AI Search with LangChain\n",
    "\n",
    "This code demonstrates how to use Azure AI Search with OpenAI and Langchain."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up a Python virtual environment in Visual Studio Code\n",
    "\n",
    "1. Open the Command Palette (Ctrl+Shift+P).\n",
    "1. Search for **Python: Create Environment**.\n",
    "1. Select **Venv**.\n",
    "1. Select a Python interpreter. Choose 3.10 or later.\n",
    "\n",
    "It can take a minute to set up. If you run into problems, see [Python environments in VS Code](https://code.visualstudio.com/docs/python/environments)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install packages\n",
    "\n",
    "If you get an OS permission error, add `--user` to the command line."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "! pip install -r azure-search-vector-python-langchain-sample-requirements.txt --quiet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load .env file\n",
    "\n",
    "Copy `/code/.env-sample` to an `.env` file in the sample folder, and update accordingly. The search service and Azure OpenAI resource and model must exist, but the search index is created and loaded during code execution. Provide a unique name for the index. Endpoints and API keys can be found in the Azure portal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from azure.identity import DefaultAzureCredential\n",
    "import os\n",
    "\n",
    "load_dotenv(override=True) # take environment variables from .env.\n",
    "\n",
    "# Variables not used here do not need to be updated in your .env file\n",
    "endpoint = os.environ[\"AZURE_SEARCH_SERVICE_ENDPOINT\"]\n",
    "key_credential = os.environ[\"AZURE_SEARCH_ADMIN_KEY\"] if len(os.environ[\"AZURE_SEARCH_ADMIN_KEY\"]) > 0 else None\n",
    "index_name = os.environ[\"AZURE_SEARCH_INDEX\"]\n",
    "azure_openai_endpoint = os.environ[\"AZURE_OPENAI_ENDPOINT\"]\n",
    "azure_openai_key = os.environ[\"AZURE_OPENAI_KEY\"] if len(os.environ[\"AZURE_OPENAI_KEY\"]) > 0 else None\n",
    "azure_openai_embedding_deployment = os.environ[\"AZURE_OPENAI_EMBEDDING_DEPLOYMENT\"]\n",
    "azure_openai_api_version = os.environ[\"AZURE_OPENAI_API_VERSION\"]\n",
    "\n",
    "credential = key_credential or DefaultAzureCredential()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create LangChain Azure OpenAI Embeddings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import AzureOpenAIEmbeddings\n",
    "from azure.identity import DefaultAzureCredential, get_bearer_token_provider\n",
    "\n",
    "openai_credential = DefaultAzureCredential()\n",
    "token_provider = get_bearer_token_provider(openai_credential, \"https://cognitiveservices.azure.com/.default\")\n",
    "\n",
    "# Use API key if provided, otherwise use RBAC authentication\n",
    "embeddings = AzureOpenAIEmbeddings(\n",
    "    azure_deployment=azure_openai_embedding_deployment,\n",
    "    openai_api_version=azure_openai_api_version,\n",
    "    azure_endpoint=azure_openai_endpoint,\n",
    "    api_key=azure_openai_key,\n",
    "    azure_ad_token_provider=token_provider if not azure_openai_key else None\n",
    ")   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create LangChain Vector Store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.vectorstores.azuresearch import AzureSearch\n",
    "\n",
    "vector_store = AzureSearch(\n",
    "    azure_search_endpoint=endpoint,\n",
    "    azure_search_key=key_credential,\n",
    "    index_name=index_name,\n",
    "    embedding_function=embeddings.embed_query,\n",
    "    semantic_configuration_name=\"default\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Indexed Benefit_Options.pdf\n",
      "Indexed Northwind_Health_Plus_Benefits_Details.pdf\n",
      "Indexed Northwind_Standard_Benefits_Details.pdf\n",
      "Indexed 636 chunks\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.document_loaders import PyPDFLoader\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "import os\n",
    "\n",
    "directory = os.path.join(\"..\", \"..\", \"..\", \"..\", \"data\", \"benefitdocs\")\n",
    "files = [\"Benefit_Options.pdf\", \"Northwind_Health_Plus_Benefits_Details.pdf\", \"Northwind_Standard_Benefits_Details.pdf\"]\n",
    "total_chunks = 0\n",
    "splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n",
    "\n",
    "for file in files:\n",
    "    loader = PyPDFLoader(os.path.join(directory, file))\n",
    "    file_chunks = loader.load_and_split(splitter)\n",
    "    results = vector_store.add_documents(documents=file_chunks)\n",
    "    total_chunks += len(results)\n",
    "    print(f\"Indexed {file}\")\n",
    "print(f\"Indexed {total_chunks} chunks\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perform a vector similarity search"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------------------------------------\n",
      "Source: ..\\..\\..\\data\\documents\\Northwind_Health_Plus_Benefits_Details.pdf\n",
      "Chunk Content: is important to understand these exclusions so that you can plan your care accordingly.  \n",
      "Services Not Covered:  \n",
      "Northwind Health Plus does not cover services that are not medically necessary, such as \n",
      "cosmetic surgery or elective procedures. Additionally, services or treatments that are \n",
      "experimental or investigational are not covered under this plan.  \n",
      "Prescriptions N ot Covered: The plan does not cover prescriptions that are not medically \n",
      "necessary, certain over -the-counter medications, or prescription medications that are used \n",
      "to enhance performance in athletics.  \n",
      "Mental Health and Substance Abuse Treatment: The plan does not cover mental health or \n",
      "substance abuse treatment services provided by a non -network provider or any services \n",
      "that are not medically necessary.  \n",
      "Preventive Care: Northwind Health Plus does not cover preventive care services provided \n",
      "by a non -network  provider.  \n",
      "Tips for Avoiding Exclusions\n",
      "--------------------------------------------------------------------------------\n",
      "Source: ..\\..\\..\\data\\documents\\Northwind_Health_Plus_Benefits_Details.pdf\n",
      "Chunk Content: Specialty Care Services: Northwind  Health Plus covers specialty care services, such as \n",
      "physical therapy, occupational therapy, and mental health services. The plan also covers \n",
      "services provided by specialists, such as cardiologists, endocrinologists, and neurologists.  \n",
      "Exceptions: Northwin d Health Plus does not cover services that are not medically \n",
      "necessary, such as cosmetic surgery, elective treatments, and experimental treatments. In \n",
      "addition, the plan does not cover services for conditions that are not covered by the plan, \n",
      "such as pre -existing conditions.\n",
      "--------------------------------------------------------------------------------\n",
      "Source: ..\\..\\..\\data\\documents\\Benefit_Options.pdf\n",
      "Chunk Content: Both plans offer coverage for pre scription drugs. Northwind Health Plus offers a wider range of \n",
      "prescription drug coverage than Northwind Standard. Northwind Health Plus covers generic, brand -\n",
      "name, and specialty drugs, while Northwind Standard only covers generic and brand -name drugs. \n",
      "Both plans offer coverage for vision and dental services. Northwind Health Plus offers coverage for vision \n",
      "exams, glasses, and contact lenses, as well as dental exams, cleanings, and fillings. Northwind Standard \n",
      "only offers coverage for vision exams and glas ses. \n",
      "Both plans offer coverage for medical services. Northwind Health Plus offers coverage for hospital stays, \n",
      "doctor visits, lab tests, and X -rays. Northwind Standard only offers coverage for doctor visits and lab \n",
      "tests. \n",
      "Northwind Health Plus is a compr ehensive plan that offers more coverage than Northwind Standard. \n",
      "Northwind Health Plus offers coverage for emergency services, mental health and substance abuse\n"
     ]
    }
   ],
   "source": [
    "# Perform a similarity search\n",
    "docs = vector_store.similarity_search(\n",
    "    \"What is included in my Northwind Health Plus plan that is not in standard?\",\n",
    "    k=50,\n",
    "    search_type=\"similarity\",\n",
    ")\n",
    "docs = docs[:3]\n",
    "for doc in docs:\n",
    "    print(\"-\" * 80)  \n",
    "    print(f\"Source: {doc.metadata['source']}\")\n",
    "    print(f\"Chunk Content: {doc.page_content}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perform a hybrid search"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------------------------------------\n",
      "Source: ..\\..\\..\\data\\documents\\Northwind_Standard_Benefits_Details.pdf\n",
      "Chunk Content: provider to see if they are part of the Northwind Health network. These services are not \n",
      "covered by the Northwind Standard plan.  \n",
      "4. If you require services from a provider that is not part of the Northwind Healt h network, \n",
      "you will be responsible for the full cost of those services.  \n",
      "By understanding the exclusions in the Northwind Standard plan, you can make informed \n",
      "decisions about your healthcare. Be sure to read the plan document carefully to make sure \n",
      "that th e plan meets your healthcare needs.  \n",
      "WHAT IF I HAVE OTHER COVERAGE?  \n",
      "Coordinating Benefits With Other Health Care Plans  \n",
      "WHAT IF I HAVE OTHER COVERAGE?  \n",
      "Coordinating Benefits With Other Health Care Plans  \n",
      "It may be possible to coordinate benefits with other health care plans if you have other \n",
      "coverage. Coordinating benefits allows you to receive payments from each health plan \n",
      "towards covered services, as long as the total amount of payments does not exceed the total \n",
      "charges for the service.\n",
      "--------------------------------------------------------------------------------\n",
      "Source: ..\\..\\..\\data\\documents\\Northwind_Standard_Benefits_Details.pdf\n",
      "Chunk Content: Summary of Benefits  \n",
      "Northwind Standard  \n",
      "Northwind Standard is a basic plan that provides coverage for medical, vision, a nd dental \n",
      "services. This plan also offers coverage for preventive care services, as well as prescription \n",
      "drug coverage. With Northwind Standard, you can choose from a variety of in -network \n",
      "providers, including primary care physicians, specialists, hospital s, and pharmacies. This \n",
      "plan does not offer coverage for emergency services, mental health and substance abuse \n",
      "coverage, or out -of-network services.  \n",
      "SUMMARY OF YOUR COSTS  \n",
      "Summary of Your Costs  \n",
      "When you choose Northwind Standard as your health plan, you can  rest assured that you \n",
      "are getting comprehensive coverage at an affordable cost. Here, we will explain the various \n",
      "costs associated with this plan so that you know what to expect when it comes to your out -\n",
      "of-pocket expenses.  \n",
      "Premiums  \n",
      "Premiums are the amou nt of money that you will need to pay each month for your coverage.\n",
      "--------------------------------------------------------------------------------\n",
      "Source: ..\\..\\..\\data\\documents\\Northwind_Health_Plus_Benefits_Details.pdf\n",
      "Chunk Content: Northwind Health Plus does not cover infert ility treatments or elective or cosmetic \n",
      "procedures. Additionally, Northwind Health Plus does not cover any services related to the \n",
      "termination of a pregnancy.  \n",
      "Tips  \n",
      "To ensure you are getting the best care possible, it is important to choose a doctor who i s in-\n",
      "network and who is experienced in providing prenatal and post -natal care. Additionally, it is \n",
      "important to familiarize yourself with the coverage provided by Northwind Health Plus and \n",
      "be aware of any out -of-pocket expenses you may be responsible for. Finally, it is important \n",
      "to get regular check -ups throughout your pregnancy to make sure you and your baby are \n",
      "healthy and safe.  \n",
      "Medical Foods  \n",
      "COVERED SERVICES: Medical Foods  \n",
      "At Contoso, we are proud to provide our employees with access to Northwind Health  Plus, a \n",
      "comprehensive insurance plan that covers a variety of medical services. Included in this\n"
     ]
    }
   ],
   "source": [
    "# Perform a hybrid search\n",
    "docs = vector_store.similarity_search(\n",
    "    query=\"What is included in my Northwind Health Plus plan that is not in standard?\",\n",
    "    k=50, \n",
    "    search_type=\"hybrid\"\n",
    ")\n",
    "docs = docs[:3]\n",
    "for doc in docs:\n",
    "    print(\"-\" * 80)  \n",
    "    print(f\"Source: {doc.metadata['source']}\")\n",
    "    print(f\"Chunk Content: {doc.page_content}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perform a hybrid search with semantic reranking (powered by Bing)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------------------------------------\n",
      "Content: Northwind Standard, you can choose from a variety of in -network providers, including primary care \n",
      "physicians, specialists, hospitals, and pharmacies. This plan  does not offer coverage for emergency \n",
      "services, mental health and substance abuse coverage, or out -of-network services.\n",
      "Comparison of Plans \n",
      "Both plans offer coverage for routine physicals, well -child visits, immunizations, and other preventive \n",
      "care servi ces. The plans also cover preventive care services such as mammograms, colonoscopies, and \n",
      "other cancer screenings. \n",
      "Northwind Health Plus offers more comprehensive coverage than Northwind Standard. This plan offers \n",
      "coverage for emergency services, both in -network and out -of-network, as well as mental health and \n",
      "substance abuse coverage. Northwind Standard does not offer coverage for emergency services, mental \n",
      "health and substance abuse coverage, or out -of-network services.\n",
      "Score: 0.024001073092222214\n",
      "Caption: Northwind Standard does not offer<em> coverage for emergency services, mental  health and substance abuse coverage,</em> or out -of-network services..\u0000\n",
      "--------------------------------------------------------------------------------\n",
      "Content: is important to understand these exclusions so that you can plan your care accordingly.  \n",
      "Services Not Covered:  \n",
      "Northwind Health Plus does not cover services that are not medically necessary, such as \n",
      "cosmetic surgery or elective procedures. Additionally, services or treatments that are \n",
      "experimental or investigational are not covered under this plan.  \n",
      "Prescriptions N ot Covered: The plan does not cover prescriptions that are not medically \n",
      "necessary, certain over -the-counter medications, or prescription medications that are used \n",
      "to enhance performance in athletics.  \n",
      "Mental Health and Substance Abuse Treatment: The plan does not cover mental health or \n",
      "substance abuse treatment services provided by a non -network provider or any services \n",
      "that are not medically necessary.  \n",
      "Preventive Care: Northwind Health Plus does not cover preventive care services provided \n",
      "by a non -network  provider.  \n",
      "Tips for Avoiding Exclusions\n",
      "Score: 0.02109144628047943\n",
      "Caption: is important to understand these exclusions so that you can plan your care accordingly.<em> Services Not</em> Covered:<em>   Northwind Health Plus does not</em> cover services that are not medically necessary, such as  cosmetic surgery or elective procedures.\n",
      "--------------------------------------------------------------------------------\n",
      "Content: Out-of-Network Services: The Northwind Standard plan d oes not cover any services that are \n",
      "provided by a provider that is not part of the Northwind Health network. This includes \n",
      "doctors, hospitals, and other healthcare providers who are not part of the Northwind Health \n",
      "network.  \n",
      "Tips  \n",
      "When selecting a healthcar e plan, it is important to be aware of the exclusions in the plan. \n",
      "Here are some tips to help you understand the exclusions in the Northwind Standard plan:  \n",
      "1. Understand the types of services that are not covered by the Northwind Standard plan. Be \n",
      "sure to  familiarize yourself with the list of exclusions and make sure that any services you \n",
      "might require are covered.  \n",
      "2. If you require emergency services, be sure to check with your provider to see if they are \n",
      "part of the Northwind Health network. If they are not, you will be responsible for the full \n",
      "cost of those services.  \n",
      "3. If you require mental health or substance a buse treatments, be sure to check with your\n",
      "Score: 0.01990460604429245\n",
      "Caption: Here are some tips to help you understand<em> the exclusions in the Northwind Standard plan:</em>   1. Understand the types of<em> services that are not covered</em> by<em> the Northwind Standard plan.</em> Be  sure to  familiarize yourself with<em> the</em> list of<em> exclusions</em> and make sure that any services you  might require are covered. 2.\n"
     ]
    }
   ],
   "source": [
    "# Perform a hybrid search with semantic reranking  \n",
    "docs_and_scores = vector_store.semantic_hybrid_search_with_score(  \n",
    "    query=\"What is included in my Northwind Health Plus plan that is not in standard?\",  \n",
    "    k=50,  \n",
    ")\n",
    "docs_and_scores = docs_and_scores[:3]\n",
    "# Print the results  \n",
    "for doc, score in docs_and_scores:  \n",
    "    print(\"-\" * 80)  \n",
    "    answers = doc.metadata['answers']  \n",
    "    if answers:  \n",
    "        if answers.get('highlights'):  \n",
    "            print(f\"Semantic Answer: {answers['highlights']}\")  \n",
    "        else:  \n",
    "            print(f\"Semantic Answer: {answers['text']}\")  \n",
    "        print(f\"Semantic Answer Score: {score}\")  \n",
    "    print(\"Content:\", doc.page_content)  \n",
    "    captions = doc.metadata['captions']\n",
    "    print(f\"Score: {score}\") \n",
    "    if captions:  \n",
    "        if captions.get('highlights'):  \n",
    "            print(f\"Caption: {captions['highlights']}\")  \n",
    "        else:  \n",
    "            print(f\"Caption: {captions['text']}\")  \n",
    "    else:  \n",
    "        print(\"Caption not available\")  \n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
